﻿using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
using DeepEarth.Toolkit.Common;
using ProjNet.CoordinateSystems;
using ProjNet.CoordinateSystems.Transformations;
using GeographicCoordinateSystem = ProjNet.CoordinateSystems.GeographicCoordinateSystem;
using ProjectedCoordinateSystem = ProjNet.CoordinateSystems.ProjectedCoordinateSystem;
using DeepEarth.Core;

namespace DeepEarth.Toolkit.Controls
{
    public class CoordinateConverter : IValueConverter
    {
        public static LocationUtm ConvertToUTM(Location location)
        {
            var ctfac = new CoordinateTransformationFactory();
            var wgs84geo = GeographicCoordinateSystem.WGS84;
            var zone = (int)Math.Ceiling((location.Longitude + 180) / 6);
            var utm = ProjectedCoordinateSystem.WGS84_UTM(zone, location.Latitude > 0);
            var trans = ctfac.CreateFromCoordinateSystems(wgs84geo, utm);
            Point pUtm = trans.MathTransform.Transform(new Point(location.Longitude, location.Latitude));

            LocationUtm retVal = new LocationUtm
            {
                Easting = pUtm.X,
                Northing = pUtm.Y,
                Zone = zone
            };

            return retVal;
        }

        public static object Convert(object value)
        {
            return Convert(value, true);
        }

        public static object Convert(object value, bool unicode)
        {
            var location = value as LocationWithFormat;

            if (location == null) return "";

            switch (location.LocationFormat)
            {
                case DeepEarth.Core.CoordinateSystem.DDMMSS: //DDMMSS
                    return Location.DDMMSSFromDecimal(location.Latitude, unicode) +
                           ((location.Latitude > 0) ? "N " : "S ") + " " +
                           Location.DDMMSSFromDecimal(location.Longitude, unicode) +
                           ((location.Longitude > 0) ? "E " : "W ");

                case DeepEarth.Core.CoordinateSystem.UTM: //UTM
                    var ctfac = new CoordinateTransformationFactory();
                    var wgs84geo = GeographicCoordinateSystem.WGS84;
                    var zone = (int)Math.Ceiling((location.Longitude + 180) / 6);
                    var utm = ProjectedCoordinateSystem.WGS84_UTM(zone, location.Latitude > 0);
                    var trans = ctfac.CreateFromCoordinateSystems(wgs84geo, utm);
                    Point pUtm = trans.MathTransform.Transform(new Point(location.Longitude, location.Latitude));
                    return string.Format("E{0} ", Math.Round(pUtm.X, 0)) + " " +
                           string.Format("N{0}", Math.Round(pUtm.Y, 0)) +
                           string.Format(" ({0}{1})", zone, location.Latitude > 0 ? 'N' : 'S');


                default: //Decimal Degrees (0)
                    return string.Format("{0:N4},", location.Latitude) + " " +
                           string.Format("{0:N4}", location.Longitude);
            }
        }

        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return Convert(value);
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            var location = value as string;
            if (location == null) return null;

            //determine format based on string

            var north = location.Contains("N");
            var south = location.Contains("S");
            if (north || south)
            {
                var DDMMSSstring = "" + '\u00B0';
                if (location.Contains(DDMMSSstring))
                {
                    //DDMMSS
                    string[] parts = location.Split('N', 'S');
                    if (parts.Length == 2)
                    {
                        var lat = Location.DecimalFromDDMMSS(parts[0]) * (north ? 1 : -1);
                        var lon = Location.DecimalFromDDMMSS(parts[1]);

                        if (lat <= 90 && lat >= -90 && lon <= 180 && lon >= -180)
                        {
                            return new LocationWithFormat {Latitude = lat.GetValueOrDefault(), Longitude = lon.GetValueOrDefault(), LocationFormat = DeepEarth.Core.CoordinateSystem.DDMMSS};
                        }
                    }
                }
                else
                {
                    try
                    {
                        //UTM
                        north = location.Contains("N)");

                        var split1 = location.IndexOf('E');
                        var split2 = location.IndexOf('N');
                        var split3 = location.IndexOf('(');
                        var split4 = location.IndexOf(')');

                        var X = double.Parse(location.Substring(split1 + 1, split2 - (split1 + 1)));
                        var Y = double.Parse(location.Substring(split2 + 1, split3 - (split2 + 1)));
                        var zone = int.Parse(location.Substring(split3 + 1, split4 - (split3 + 2)));

                        var ctfac = new CoordinateTransformationFactory();
                        var wgs84geo = GeographicCoordinateSystem.WGS84;
                        var utm = ProjectedCoordinateSystem.WGS84_UTM(zone, north);
                        var trans = ctfac.CreateFromCoordinateSystems(utm, wgs84geo);

                        Point pwgs84geo = trans.MathTransform.Transform(new Point(X, Y));
                        return new LocationWithFormat { Latitude = pwgs84geo.Y, Longitude = pwgs84geo.X, LocationFormat = DeepEarth.Core.CoordinateSystem.UTM };
                    }
                    catch (Exception)
                    {
                        return null;
                    }
                }
            }
            else
            {
                //must be decimal degrees, split on comma
                string[] parts = location.Split(',');
                if (parts.Length == 2)
                {
                    double lat;
                    double lon;
                    if (double.TryParse(parts[0], out lat) && double.TryParse(parts[1], out lon))
                    {
                        if (lat <= 90 && lat >= -90 && lon <= 180 && lon >= -180)
                        {
                            return new LocationWithFormat {Latitude = lat, Longitude = lon, LocationFormat = DeepEarth.Core.CoordinateSystem.Degrees};
                        }
                    }
                }
            }
            return null;

        }
    }

    public class CoordinateModeConverter : IValueConverter
    {
        public static object Convert(object value, Type targetType)
        {
            if (targetType == typeof(DeepEarth.Core.CoordinateSystem))
            {
                var retval = Enum.Parse(typeof(DeepEarth.Core.CoordinateSystem), value.ToString(), true);
                return retval;
            }
            else
            {
                var retval = (int)value;
                return retval;
            }
        }

        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return Convert(value, targetType);
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return Convert(value, targetType);
        }
    }

}
